using System;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Runtime.Serialization;
using Microsoft.Xna.Framework.Graphics;

namespace INdT.Sparta.Graphics
{
    [DataContract]
    public class SpartaSprite : SpartaTexture
    {
        private int animationCounter;
        private Rectangle sourceRect;
        private int currentFrame;

        [DataMember]
        public int AnimationCounter
        {
            get { return animationCounter; }
            set
            {
                animationCounter = value;
                if (!string.IsNullOrEmpty(CurrentAnimation) && animationCounter >= Animations[CurrentAnimation].Length)
                {
                    if (Looping)
                    {
                        animationCounter = 0;
                        if (OnAnimationCompleted != null)
                        {
                            OnAnimationCompleted(this);
                        }
                    }
                    else
                    {
                        IsPlaying = false;
                        animationCounter = Animations[CurrentAnimation].Length - 1;
                        if (OnAnimationCompleted != null)
                        {
                            OnAnimationCompleted(this);
                        }
                    }
                }
            }
        }

        [DataMember]
        public override Rectangle SourceRect
        {
            get { return sourceRect; }
            set { sourceRect = value; }
        }

        [DataMember]
        public int CurrentFrame
        {
            get { return currentFrame; }
            set
            {
                currentFrame = value;

                if (this.Texture != null)
                {
                    if (this.SourceRect.Width > 0)
                    {
                        var frameCountX = this.Texture.Width / this.SourceRect.Width;
                        var frameY = (int)Math.Floor((double)(this.currentFrame / frameCountX));
                        sourceRect.X = this.SourceRect.Width * (this.currentFrame - (frameY * frameCountX));
                        sourceRect.Y = this.SourceRect.Height * frameY;
                    }
                    else
                    {
                        sourceRect.X = currentFrame * SourceRect.Width;
                    }
                }
            }
        }

        [DataMember]
        public Dictionary<string, int[]> Animations { get; set; }

        [DataMember]
        public string CurrentAnimation { get; set; }

        [DataMember]
        public bool Looping { get; set; }

        [DataMember]
        public int FrameTime { get; set; }

        [DataMember]
        public int ElapsedTime { get; set; }

        [DataMember]
        public bool IsPlaying { get; set; }

        public Action<SpartaSprite> OnAnimationCompleted;

        public SpartaSprite()
            : this(0, 0, string.Empty)
        {
        }

        public SpartaSprite(string imageName)
            : this(0, 0, imageName)
        {
        }

        public SpartaSprite(int width, int height, string imageName)
        {
            AnimationCounter = 0;
            CurrentFrame = 0;
            Looping = true;
            FrameTime = 50;
            ElapsedTime = 0;
            Animations = new Dictionary<string, int[]>();

            SourceRect = new Rectangle(0, 0, width, height);
            TextureAssetName = imageName;

            OnAnimationCompleted = null;
        }

        public void AddAnimation(string animationName, int[] animationArray)
        {
            if (!Animations.ContainsKey(animationName))
            {
                Animations.Add(animationName, animationArray);
            }
        }

        public void Play(string animationName, bool looping)
        {
            Play(animationName, looping, SpriteEffects.None, 50);
        }

        public void Play(string animationName, bool looping, SpriteEffects spriteEffect)
        {
            Play(animationName, looping, spriteEffect, 50);
        }

        public void Play(string animationName, bool looping, SpriteEffects spriteEffect, int frameTime)
        {
            if (Animations.ContainsKey(animationName))
            {
                IsPlaying = true;
                CurrentAnimation = animationName;
                AnimationCounter = 0;
                this.Looping = looping;
                this.FrameTime = frameTime;
                this.SpriteEffect = spriteEffect;
                this.ElapsedTime = 0;
            }
        }

        public void Stop()
        {
            IsPlaying = false;
            Reset();
        }

        public virtual void Reset()
        {
            AnimationCounter = 0;
            ElapsedTime = 0;
        }

        public override void Update(GameTime gameTime)
        {
            if (IsPlaying)
            {
                ElapsedTime += (int)gameTime.ElapsedGameTime.TotalMilliseconds;
                if (ElapsedTime > FrameTime && Animations.Count > 0)
                {
                    CurrentFrame = Animations[CurrentAnimation][AnimationCounter];
                    AnimationCounter++;
                    ElapsedTime = 0;
                }
            }
        }

        public override void Dispose()
        {
            if (IsDisposed)
            {
                return;
            }

            foreach (KeyValuePair<string, int[]> animation in Animations)
            {
                GC.SuppressFinalize(animation.Value);
            }
            Animations.Clear();
            GC.SuppressFinalize(Animations);
            Animations = null;
            base.Dispose();
        }
    }
}
